#!/usr/bin/env python
# -*- coding:utf-8 -*-
import uuid
import os
import sys
import pickle
from requests import post
from utils import MD5
import time
import threading

import logging
logging.basicConfig(level=logging.INFO)

conf = "registry.conf"
server_url = "http://192.168.1.105:8001/check/info/"
repeat_time = 5


class MachineInfo(object):
    def get_mac_address(self):
        """
        Obtain node's MAC
        :return:
        """
        mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
        return ":".join([mac[e:e+2] for e in range(0, 11, 2)]).strip()

    def get_cpu_id(self):
        """
        Obtain cpu id
        :return:
        """
        f = os.popen("sudo dmidecode -t 1 | grep ID")  # centos

        cpu_id = f.readline()
        if not cpu_id:
            f = os.popen("sudo dmidecode -t 4 | grep ID")  # ubuntu
            cpu_id = f.readline()
            if cpu_id:
                return cpu_id.strip()
            else:
                """
                If we cant't find cpuid, we return a random number?
                """
                return "Can not find cpu id"
        elif cpu_id:
            return cpu_id.strip()

    def get_motherboard_information(self):
        """
        Obtain motherboard information
        :return:
        """
        f = os.popen("sudo dmidecode -t 1 | grep Serial")
        motherboard_information_id = f.readline()

        if not motherboard_information_id:
            f = os.popen("sudo dmidecode -t 2 | grep Serial")
            motherboard_information_id = f.readline()
            if not motherboard_information_id:
                return motherboard_information_id.strip()

            else:
                """
                If we cant't find motherboard information id, we return a random number?
                """
                return "Motherboard Serial Can Not Find"

        elif motherboard_information_id:
            return motherboard_information_id.strip()

    def get_linux_network(self):
        network_list = os.popen("curl members.3322.org/dyndns/getip").readlines()
        try:
            return network_list[0].strip()
        except IndexError as e:
            print("====>")

    def get_machine_info(self):
        cpu_id = MD5.encryption(self.get_cpu_id())
        mac = self.get_mac_address()
        motherboard_serial = MD5.encryption(self.get_motherboard_information())
        network = self.get_linux_network()

        return {
            "cpu_id": cpu_id,
            "mac": mac,
            "motherboard_serial": motherboard_serial,
            "network": network
        }


class SerializerDeSerializer(object):

    @classmethod
    def deserializer(cls, file):
        try:
            config = open(file, "rb")
            deser = pickle.load(config, encoding="utf-8")
            return {"package_id": deser.package_id,
                    "cpu_id": deser.cpu_id,
                    "mac": deser.mac,
                    "motherboard_serial": deser.motherboard_serial,
                    "network": deser.network}
        except FileNotFoundError as e:
            print("File Can Not Find")
        else:
            config.close()


class RegistryInformation(object):
    """
    This class is used for persistence registry information
    """
    def __init__(self, package_id="", cpu_id="", mac="", motherboard_serial="", network=""):
        self.package_id = package_id
        self.cpu_id = cpu_id
        self.mac = mac
        self.motherboard_serial = motherboard_serial
        self.network = network

    def __str__(self):
        return {
            "package_id": self.package_id,
            "cpu_id": self.cpu_id,
            "mac": self.mac,
            "motherboard_serial": self.motherboard_serial,
            "network": self.network
            }


class Identifications(object):
    c = MachineInfo()
    machine_info = c.get_machine_info()
    conf_info = SerializerDeSerializer.deserializer(conf)

    def get_machine_info(self):
        """
        获取客户机信息
        如果任何一个信息为空，则返回True
        :return:
        """

        if self.machine_info["cpu_id"] == '' \
           and self.machine_info["mac"] == '' \
           and self.machine_info["motherboard_serial"] == '' \
           and self.machine_info["network"] == '':
            logging.info('can not get machine_info')
            return True
        logging.info('got machine_info')
        return False

    def identify_config(self):
        """
        对比安装包信息
        如果任意信息不匹配则返回True
        :return:
        """

        if self.conf_info["cpu_id"] != self.machine_info["cpu_id"]\
            or self.conf_info["mac"] != self.machine_info["mac"]\
                or self.conf_info["motherboard_serial"] != self.machine_info["motherboard_serial"]\
                or self.conf_info["network"] != self.machine_info["network"]:
            logging.info('local identification failed')
            return True
        logging.info('local identification passed')
        return False

    def check_service_info(self):
        """
        对比服务器信息

        :return:
        """

        self.machine_info["package_id"] = self.conf_info["package_id"]
        resp = post(url=server_url, json=self.machine_info)
        logging.info(resp)

        characters = resp.content.decode('UTF-8')
        characters = characters.strip()
        logging.info(characters)
        if characters == 'true':
            return True
        if characters == 'false':
            return False
        return True


class Executing(object):
    flag = False

    def execute_identification(self):
        """
        定时执行验证
        :return:
        """

        while not self.flag:
            instance = Identifications()
            self.flag = (instance.get_machine_info()
                         or instance.identify_config()
                         or instance.check_service_info())

            if self.flag:
                break
            time.sleep(repeat_time)

    def start(self):
        t = threading.Thread(target=self.execute_identification)
        t.start()
        t.join()


if __name__ == "__main__":
    e = Executing()
    e.start()
    logging.info(e.flag)
